package personal.parser;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

class SimpleItem implements ParserFA.Item {


    /**
     * @return 返回当前符号右侧的表达式，用于求 first 以求 lookahead
     */
    public List<Grammar.Symbol> getFollowSymbols() {
        if (this.point == this.production().rightSide().size())
            throw new UnknownError("对可归约项调用 getFollowSymbols");
        if (this.point + 1 == this.production().rightSide().size()) return new ArrayList<>(List.of(Grammar.EMPTY));
        List<Grammar.Symbol> ret = new ArrayList<>();
        for (int i = this.point + 1; i < this.production().rightSide().size(); ++i) {
            ret.add(this.production().rightSide().get(i));
        }
        return ret;
    }

    /**
     * @return 当前位置向后移动一位
     */
    public SimpleItem getNextItem() {
        return new SimpleItem(this.production, this.point + 1, this.lookahead);
    }

    /**
     * 合并 lookahead
     *
     * @param o 另一个产生式
     * @throws IllegalArgumentException 产生式或当前符号不同，无法合并
     */
    public void merged(SimpleItem o) {
        if (this.production != o.production || this.point != o.point)
            throw new IllegalArgumentException("产生式或当前符号不同，无法合并");
        this.lookahead.addAll(o.lookahead);
    }

    /**
     * 构造函数，创建一个指定产生式从头开始的 item
     *
     * @param production 指定产生式
     * @param lookahead  lookahead
     */
    public SimpleItem(Grammar.Production production, Set<Grammar.Terminal> lookahead) {
        this(production, 0, lookahead);
    }

    /**
     * 完全的构造函数，供内部使用
     *
     * @param production 指定产生式
     * @param point      当前位置
     * @param lookahead  lookahead
     */
    private SimpleItem(Grammar.Production production, int point, Set<Grammar.Terminal> lookahead) {
        this.production = production;
        this.point = point;
        this.lookahead = lookahead;
    }

    private final Grammar.Production production;
    private final int point;
    private final Set<Grammar.Terminal> lookahead;

    @Override
    public Grammar.Production production() {
        return this.production;
    }

    public int point() {
        return this.point;
    }

    @Override
    public Set<Grammar.Terminal> lookahead() {
        return this.lookahead;
    }

    @Override
    public Grammar.Symbol getCurrent() throws IndexOutOfBoundsException {
        return this.production().rightSide().get(this.point);
    }

    @Override
    public Grammar.Symbol getCurrentOrNull() {
        if (this.point >= this.production().rightSide().size()) return null;
        return this.getCurrent();
    }

    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        ret.append(this.production.leftSide()).append("  -->  ");
        for (int i = 0; i < this.production.rightSide().size(); ++i) {
            Grammar.Symbol symbol = this.production.rightSide().get(i);
            if (i == this.point) ret.append("•  ");
            ret.append(symbol).append("  ");
        }
        if (this.point == this.production.rightSide().size()) ret.append("•  ");
        ret.append(lookahead);
        return ret.toString();
    }

    @Override
    public int hashCode() {
        return this.production.hashCode() + this.point + this.lookahead.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null || this.getClass() != obj.getClass()) return false;
        SimpleItem other = (SimpleItem) obj;
        return this.production.equals(other.production) && this.point == other.point && this.lookahead.equals(other.lookahead);

    }
}
